---------------------------------------------------------------------------
-- Company     : Vim Inc
-- Author(s)   : Fabien Marteau
-- 
-- Creation Date : 01/05/2008
-- File          : Wb_pwm.vhd
--
-- Abstract : A simple IP to drive pwm
-- 
--0x00 : |speed(7	downto 0)|
--0x01 : |dir|x|x|x|x|x|speed(9 downto 8)|
--
---------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;

---------------------------------------------------------------------------
Entity Wb_pwm is 
---------------------------------------------------------------------------
generic(
	-- fpga frequency at 50MHz
	constant freq_fpga : natural := 50000000;
	-- pwm frequency  at 10kHz
	constant freq_pwm  : natural := 10000
);
port 
(
	-- syscon signals
	reset_n : in std_logic ;
	clk : in std_logic ;
	--Wishbone signals
	wbs_address   : in std_logic ;
	wbs_writedata : in std_logic_vector( 7 downto 0);
	wbs_readdata  : out std_logic_vector( 7 downto 0);
	wbs_strobe    : in std_logic ;
	wbs_write     : in std_logic ;
	wbs_ack       : out std_logic ;
	-- output
	pwm           : out std_logic ;
	pwm_dir       : out std_logic 

);
end entity;


---------------------------------------------------------------------------
Architecture Wb_pwm_1 of Wb_pwm is
---------------------------------------------------------------------------
	signal speed   : std_logic_vector( 9 downto 0);
	signal dir     : std_logic ;
	signal refresh : std_logic ;
	signal slow_clk : std_logic; -- clock for pwm generation
	signal debug   : std_logic ;
	signal pwmdebug: natural;
begin

	wbs_ack <=  wbs_strobe;
	pwm_dir <= dir;

	-- Write value
	writeproc : process (clk,reset_n)
	begin
		if reset_n = '0' then
			speed <= (others => '0');
			dir   <= '0';
			refresh<= '0';
		elsif rising_edge(clk) then
			refresh <= '0';
			if ((wbs_strobe and wbs_write) = '1') then -- if write
				if(wbs_address = '0') then -- address decoding
					speed(7 downto 0) <= wbs_writedata;
					dir <= dir;
				else
					speed(9 downto 8) <= wbs_writedata(1 downto 0);
					dir               <= wbs_writedata(7);
					refresh <= '1';
				end if;
			end if;
		end if;
	end process writeproc;

	--read value
	readproc : process(clk,reset_n)
	begin
		if reset_n = '0' then
			wbs_readdata <= (others => '0');
		elsif rising_edge(clk) then
			if(wbs_strobe and (not wbs_write)) = '1' then -- if read
				if (wbs_address = '0') then -- address decoding
					wbs_readdata <= speed(7 downto 0);
				else
					wbs_readdata <= dir&"00000"&speed(9 downto 8);
				end if;
			end if;
		end if;
	end process readproc;

	-- under frequency clock generation (here 2.5MHz) 
	clk_pwm : process (clk,reset_n)
		variable cmpt : natural range 1 to (freq_fpga/(256 * 2 * freq_pwm));
	begin
		if reset_n = '0' then
			slow_clk <= '0';
			cmpt := 1;
		elsif rising_edge(clk) then
			cmpt := cmpt + 1;
			if (cmpt = (freq_fpga / (512 *2* freq_pwm))) then
				slow_clk <= not slow_clk;
				cmpt := 1;
			end if;
		end if;
	end process clk_pwm;

	-- pwm generation
	pwmproc : process (clk,reset_n)
		variable cmpt_pwm : natural range 0 to 1023;
		variable slow_clk_old : std_logic ;
		variable speedpwm : natural range 0 to 1023;
	begin
		if reset_n = '0' then
			pwm <= '0';
			cmpt_pwm := 0;
			slow_clk_old := slow_clk;
			speedpwm := 0;
		elsif rising_edge(clk) then
			debug <= '0';
			-- if new speed value wrote
			if refresh = '1' then 
				speedpwm := to_integer(unsigned(speed));
				debug <= '1';
			end if;
			-- manage pwm on rising edge of slow_clk
			if slow_clk_old = '0' and slow_clk = '1' then
				cmpt_pwm := cmpt_pwm + 1;
				pwmdebug <= cmpt_pwm;
				if(cmpt_pwm = speedpwm) then
					pwm <= '0';
					-- TODO: better function
				elsif cmpt_pwm = 1023 then -- When buffer overflow
					cmpt_pwm := 0;
					debug <= '1';
					pwm <= '1';
				end if;
			end if;
			slow_clk_old := slow_clk;
		end if ;
	end process pwmproc;

end architecture Wb_pwm_1;

